package com.xhj.mybatis.plugin;

import com.xhj.mybatis.executor.statement.StatementHandler;
import com.xhj.mybatis.mapping.BoundSql;
import com.xhj.mybatis.reflection.MetaObject;
import com.xhj.mybatis.reflection.SystemMetaObject;
import com.xhj.mybatis.session.Configuration;
import com.xhj.mybatis.session.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;

/**
 * StatementHandlerInterceptor
 *
 * @author XJks
 * @description 处理 StatementHandler 类的拦截器插件类，用于在执行 SQL 语句时记录日志信息。
 */
@Intercepts({
        // 拦截 prepare 方法
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class}),
        // 拦截 parameterize 方法
        @Signature(type = StatementHandler.class, method = "parameterize", args = {Statement.class}),
        // 拦截 update 方法
        @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
        // 拦截 query 方法
        @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class})
})
public class StatementHandlerInterceptor implements Interceptor {
    /**
     * 日志记录器
     */
    private static final Logger log = LoggerFactory.getLogger(StatementHandlerInterceptor.class);

    /**
     * 方法处理器映射表
     */
    private static final Map<Method, InterceptMethodHandler> methodHandlerMap;

    /**
     * 静态代码块，初始化方法处理器映射表
     */
    static {
        methodHandlerMap = new HashMap<>();
        try {
            // 处理 prepare 方法
            Method prepare = StatementHandler.class.getDeclaredMethod("prepare", Connection.class);
            methodHandlerMap.put(prepare, StatementHandlerInterceptor::handlePrepare);
            // 处理 parameterize 方法
            Method parameterize = StatementHandler.class.getDeclaredMethod("parameterize", Statement.class);
            methodHandlerMap.put(parameterize, StatementHandlerInterceptor::handleParameterize);
            // 处理 update 方法
            Method update = StatementHandler.class.getDeclaredMethod("update", Statement.class);
            methodHandlerMap.put(update, StatementHandlerInterceptor::handleUpdate);
            // 处理 query 方法
            Method query = StatementHandler.class.getDeclaredMethod("query", Statement.class, ResultHandler.class);
            methodHandlerMap.put(query, StatementHandlerInterceptor::handleQuery);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 获取被拦截的方法
        Method method = invocation.getMethod();
        // 从映射表中获取对应的方法处理器
        InterceptMethodHandler handler = methodHandlerMap.get(method);
        // 如果找到了对应的处理器，则调用处理器进行处理
        if (handler != null) {
            return handler.handle(invocation);
        }
        // 如果没有对应的处理器，则直接执行原方法
        return invocation.proceed();
    }

    /**
     * 处理 prepare 方法的逻辑
     *
     * @param invocation 调用信息
     * @return 方法执行结果
     * @throws Throwable 可能抛出的异常
     */
    private static Object handlePrepare(Invocation invocation) throws Throwable {
        // 获取目标对象
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        // 获取 BoundSql 对象
        BoundSql boundSql = statementHandler.getBoundSql();
        // 格式化 SQL，去除多余的空白字符
        String sql = boundSql.getSql().replaceAll("\\s+", " ").trim();
        // 声明返回结果对象
        Object result = null;
        // 记录开始时间
        long start = System.nanoTime();
        try {
            // 执行被拦截的方法
            result = invocation.proceed();
            // 返回结果
            return result;
        } finally {
            // 计算执行时间，单位为毫秒
            long executionTime = (System.nanoTime() - start) / 1_000_000;
            // 记录日志信息
            log.info("[StatementHandler] prepare | executionTime={}ms | sql=\"{}\"", executionTime, sql);
        }
    }

    /**
     * 处理 parameterize 方法的逻辑
     *
     * @param invocation 调用信息
     * @return 方法执行结果
     * @throws Throwable 可能抛出的异常
     */
    private static Object handleParameterize(Invocation invocation) throws Throwable {
        // 获取目标对象
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        // 获取 BoundSql 对象
        BoundSql boundSql = statementHandler.getBoundSql();
        // 格式化 SQL，去除多余的空白字符
        String sql = boundSql.getSql().replaceAll("\\s+", " ").trim();
        // 通过 Mybatis 自定义的反射框架获取 Configuration 对象
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        Configuration configuration = (Configuration) metaObject.getValue("configuration");
        // 声明返回结果对象
        Object result = null;
        // 记录开始时间
        long start = System.nanoTime();
        try {
            // 执行被拦截的方法
            result = invocation.proceed();
            // 返回结果
            return result;
        } finally {
            // 计算执行时间，单位为毫秒
            long executionTime = (System.nanoTime() - start) / 1_000_000;
            // 获取参数对象的简要视图信息
            String parameterObjectView = InterceptorUtil.getParameterObjectView(boundSql, configuration);
            // 记录日志信息
            log.info("[StatementHandler] parameterize | executionTime={}ms | sql=\"{}\" | parameterObjectView={}", executionTime, sql, parameterObjectView);
        }
    }

    /**
     * 处理 query 方法的逻辑
     *
     * @param invocation 调用信息
     * @return 方法执行结果
     * @throws Throwable 可能抛出的异常
     */
    private static Object handleQuery(Invocation invocation) throws Throwable {
        // 获取目标对象
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        // 获取 BoundSql 对象
        BoundSql boundSql = statementHandler.getBoundSql();
        // 格式化 SQL，去除多余的空白字符
        String sql = boundSql.getSql().replaceAll("\\s+", " ").trim();
        // 声明返回结果对象
        Object result = null;
        // 记录开始时间
        long start = System.nanoTime();
        try {
            // 执行被拦截的方法
            result = invocation.proceed();
            // 返回结果
            return result;
        } finally {
            // 计算执行时间，单位为毫秒
            long executionTime = (System.nanoTime() - start) / 1_000_000;
            // 获取结果的简要视图信息
            String resultView = InterceptorUtil.getResultView(result);
            // 记录日志信息
            log.info("[StatementHandler] query | executionTime={}ms | sql=\"{}\" | resultView={}", executionTime, sql, resultView);
        }
    }

    /**
     * 处理 update 方法的逻辑
     *
     * @param invocation 调用信息
     * @return 方法执行结果
     * @throws Throwable 可能抛出的异常
     */
    private static Object handleUpdate(Invocation invocation) throws Throwable {
        // 获取目标对象
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        // 获取 BoundSql 对象
        BoundSql boundSql = statementHandler.getBoundSql();
        // 格式化 SQL，去除多余的空白字符
        String sql = boundSql.getSql().replaceAll("\\s+", " ").trim();
        // 声明返回结果对象
        Object result = null;
        // 记录开始时间
        long start = System.nanoTime();
        try {
            // 执行被拦截的方法
            result = invocation.proceed();
            // 返回结果
            return result;
        } finally {
            // 计算执行时间，单位为毫秒
            long executionTime = (System.nanoTime() - start) / 1_000_000;
            // 记录日志信息
            log.info("[StatementHandler] update | executionTime={}ms | sql=\"{}\" | rowCount={}", executionTime, sql, result);
        }
    }
}
